using System;
using System.IO;
using System.IO.IsolatedStorage;
using System.Collections;
using System.Collections.Specialized;
using System.Threading;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using XTCast.Mp3;
using XTCast.Radio;

namespace XTCast.Radio {
	
	public class PlaylistFilter : IMediaFilter {

		virtual public Track PlayingTrack {
			get {
				if (fHistory.Count > 0)
					return (Track) fHistory[0];
				else
					return null;
			}
		}

		public PlaylistFilter( Station station, StationConfig sc) {
			this.sc = sc;
			this.station = station;
			station.PlaylistFilter=this;
			station.TrackChanged+=new XTCast.Radio.Station.TrackEventDelegate(station_TrackChanged);
			station.TrackError+=new XTCast.Radio.Station.TrackEventDelegate(station_TrackError);
			fHistory = new ArrayList();
			fQueue = new ArrayList();
			InitMutex(sc.ID+".tracklist");
		}

		public override string ToString() {
			return String.Format("PL{0,13:G}", Station.ID);
		}


		public Station Station {
			get {
				return station;
			}
		}

		public StationConfig StationConfig {
			get {
				return sc;
			}
		}

		private void InitMutex(string name) {
			try {
				tracklistMutex = new System.Threading.Mutex(false,name);
			} catch (Exception ex) {
				Log.Error(this,ex);
			}
		}

		public void Start(IMediaLibrary library, IMediaIterator source) {
			library.FileChanged+=new FileChangedEventHandler(library_FileChanged);
			fLibrary = library;
			fSrc = source;

			thread = new Thread(new ThreadStart(Run));
			thread.IsBackground=true;
			thread.Priority=ThreadPriority.Lowest;
			thread.Start();
		}
		
		public Track[] GetPlaylist(int maxQueue, int maxHistory) {
			ArrayList arr=new ArrayList();

			int numQueue = System.Math.Min(maxQueue, fQueue.Count);
			int numHistory = System.Math.Min(maxHistory,fHistory.Count-1);
			for (int i = -numQueue; i <= numHistory; i++) {
				Track track;
				if (i < 0) 
					track = (Track)fQueue[-i-1];
				else
					track = (Track)fHistory[i];
				if (track != null) {
					track.Playing=(i==0);
					track.HasPlayed=(i>0);
					arr.Add(track);
				}
			}
			return (Track[])arr.ToArray(typeof(Track));
		}

		public virtual void Rearrange(Track src, Track dest) {
			lock (fQueue.SyncRoot) {
				int idxDest = fQueue.IndexOf(dest);
				fQueue.Remove(src);
				if (fHistory.Count>0 && fHistory[0] == dest) {
					PlayNow(src);
				}else{
					if (idxDest>=0) fQueue.Insert(idxDest,src);
					if (fHistory.Count>0 && fHistory[0] == src) PlayNextTrack();
				}
				RemoveFromHistory(src);
			}
		}
		
		public virtual void RemoveFromHistory(Track track) {
			lock (fHistory.SyncRoot) {
				while (fHistory.Contains(track))
					fHistory.Remove(track);
			}
		}

		public virtual void RemoveMultipleFromQueue(int numToRemove) {
			for (int i=0;i<numToRemove;i++)
				fQueue.RemoveAt(0);
			FillQueue(0);
		}

		public virtual void RemoveFromQueue(Track track) {
			lock (fQueue.SyncRoot) {
				while (fQueue.Contains(track))
					fQueue.Remove(track);
				FillQueue(0);
			}
		}
		
		public virtual void AddToQueue(Track track) {
			track.Requested = true;
			lock (fQueue.SyncRoot) {
				int index = 0;
				foreach (Track trck in fQueue) {
					if (trck!=null && !trck.Requested) {
						fQueue.Insert(index, track);
						return;
					}
					index++;
				}
				fQueue.Add(track);
			}
		}

		public virtual void  MoveUp(Track track) {
			int index=fQueue.IndexOf(track);
			fQueue.RemoveAt(index);
			fQueue.Insert(index+1, track);
		} 

		public virtual void  MoveDown(Track track) {
			int index = fQueue.IndexOf(track);
			fQueue.RemoveAt(index);
			fQueue.Insert(index-1, track);
		}
		
		public virtual void  PlayNext(Track track) {
			track.Requested = true;
			fQueue.Insert(fCurTrack, track);
		}

		void Run() {
			Log.Verbose(this,"Starting...");
			try {
				foreach (Track track in ReadPlaylist())
					fQueue.Insert(0,track);
			} 
			catch (FileNotFoundException ex) {}
			catch (Exception ex) {
				Log.Error(ex);
			}
			while(station.Run) {
				try {
					Thread.Sleep(TimeSpan.FromSeconds(30));
					WritePlaylist(GetPlaylist(99,0));
				} 
				catch (System.UnauthorizedAccessException uae) {
				}
				catch (Exception ex) {
					Log.Error(ex);
				}
			}
			Log.Verbose(this,"Stoping...");
		}

		private void WritePlaylist(Track[] playlist) {
			BinaryFormatter bf = new BinaryFormatter();
			FileStream fs = new IsolatedStorageFileStream(station.ID+".playlist",FileMode.Create,FileAccess.Write,FileShare.ReadWrite);
			try {
				bf.Serialize(fs,playlist); 
			} 
			finally {
				fs.Close();
			}
		}
		
		private Track[] ReadPlaylist() {
			BinaryFormatter bf = new BinaryFormatter();
			FileStream fs = new IsolatedStorageFileStream(station.ID+".playlist",FileMode.Open,FileAccess.Read,FileShare.ReadWrite);
			try {
				return (Track[])bf.Deserialize(fs);
			} 
			finally {
				fs.Close();
			}
		}
		
		private static ManualResetEvent trackAdvanced = new ManualResetEvent(false);
		
		public virtual void PlayNextTrack() {
			station.DJ.SkipTrack=true;
		}

		public virtual void  PlayPrevTrack() {
			if (fHistory.Count > 1) {
				Track track = (Track) fHistory[1];
				PlayNow(track);
			}
		}

		public virtual void PlayNow(Track track) {
			PlayNext(track);
			PlayNextTrack();
		}
		
		private void  FillQueue(int minLength) {
			lock (fQueue.SyncRoot) {
				while (fQueue.Count <= Math.Max(10,minLength)) {
					Track t = fSrc.GetNextTrack();
					if (t!=null)
						fQueue.Add(t);
					else
						return;
				}
			}
		}
		
		public virtual Track GetNextTrack() {
			FillQueue(fCurTrack);
			Track next;
			lock (fQueue.SyncRoot) {
				if (fCurTrack>=fQueue.Count) return null;
				next = (Track) fQueue[fCurTrack];
				fCurTrack++;
			}
			return next;
		}

		public virtual Track PeekNextTrack() {
			FillQueue(fCurTrack);
			Track next;
			lock (fQueue.SyncRoot) {
				if (fCurTrack>=fQueue.Count) return null;
				next = (Track) fQueue[fCurTrack];
			}
			return next;
		}

		private void station_TrackChanged(Object sender, XTCast.Radio.Station.TrackEventArgs e) {
			TrackChanged(e.Track);
		}

		private void station_TrackError(Object sender, XTCast.Radio.Station.TrackEventArgs e) {
			TrackError(e.Track);
		}

		public virtual void TrackError(Track track) {
			lock (fQueue.SyncRoot) {
				int pos = fQueue.IndexOf(track);
				fCurTrack = System.Math.Max(fCurTrack - 1 - pos, 0);
				fQueue.Remove(track);
			}
		}
		
		public virtual void  TrackChanged(Track track) {
			lock (fQueue.SyncRoot) {
				lock (fHistory.SyncRoot) {
					try {
						int pos = fQueue.IndexOf(track);
						fCurTrack = System.Math.Max(fCurTrack - 1 - pos, 0);
						fQueue.Remove(track);
						fHistory.Insert(0, track);						
						Log.Verbose(this, "Track=" + track.ToString());
					}
					catch (System.Exception x) {
						Log.Error(x);
					}
				}
			}
			FillQueue(0);
		}
		
		protected internal Thread thread;

		protected internal IMediaIterator fSrc;	//Upstream Music Iterator
		protected internal IMediaLibrary fLibrary;
		protected internal Station station;
		protected internal int fCurTrack = 0;
		protected internal ArrayList fQueue;
		protected internal ArrayList fHistory;
		protected internal StationConfig sc;
		protected internal const int MaxHistory = 10;
		protected internal System.Threading.Mutex tracklistMutex;

		private void library_FileChanged(MP3File f, WatcherChangeTypes ChangeType) {
			if (ChangeType==System.IO.WatcherChangeTypes.Created) {
				this.AddToQueue(new Track(f));
			}
		}
	}


}